use axum::http::StatusCode;
use axum::response::IntoResponse;
use crate::responses::CusResponse;

/// Utility function for mapping any error into a `500 Internal Server Error`
/// response.
pub fn internal_error<E>(err: E) -> (StatusCode, String)
    where
        E: std::error::Error,
{
    (StatusCode::INTERNAL_SERVER_ERROR, err.to_string())
}

#[derive(Debug)]
pub struct AppError {
    pub message: String,
    pub code: u16,
}

impl AppError {
    fn new(message: String, code: u16) -> Self {
        Self { message, code }
    }
    pub fn from_err(err: Box<dyn std::error::Error>, code: u16) -> Self {
        Self::new(err.to_string(), code)
    }
    pub fn from_string(message: String, code: u16) -> Self {
        Self::new(message, code)
    }
    pub fn from_str(msg: &str, code: u16) -> Self {
        Self::new(msg.to_string(), code)
    }
    pub fn notfound_opt(message: String) -> Self {
        Self::new(message, 404)
    }
    pub fn notfound_msg(msg: &str) -> Self {
        Self::notfound_opt(msg.to_string())
    }
    pub fn notfound() -> Self {
        Self::notfound_msg("没有找到符合条件的数据")
    }
}

impl std::fmt::Display for AppError {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "{:?}", self)
    }
}

impl std::error::Error for AppError {}

impl AppError {
    pub fn from<T>(err: T, code: u16) -> Self
        where T: std::fmt::Display + std::fmt::Debug
    {
        tracing::error!("{:#?}", err);
        Self::from_string(err.to_string(), code)
    }
}

impl From<rbatis::Error> for AppError {
    /// 数据库异常（rbatis）
    fn from(err: rbatis::Error) -> Self {
        Self::from(err, 1001)
    }
}

impl From<validator::ValidationErrors> for AppError {
    /// 数据校验异常
    fn from(err: validator::ValidationErrors) -> Self {
        Self::from(err, 2001)
    }
}

impl From<ring::error::Unspecified> for AppError {
    /// 加密异常
    fn from(err: ring::error::Unspecified) -> Self {
        Self::from(err, 3001)
    }
}

impl From<axum::extract::rejection::ExtensionRejection> for AppError {
    /// Axum异常
    fn from(err: axum::extract::rejection::ExtensionRejection) -> Self {
        Self::from(err, 4001)
    }
}

impl From<axum::extract::rejection::FormRejection> for AppError {
    /// Axum异常
    fn from(err: axum::extract::rejection::FormRejection) -> Self {
        Self::from(err, 4002)
    }
}

impl From<axum::extract::rejection::JsonRejection> for AppError {
    /// Axum异常
    fn from(err: axum::extract::rejection::JsonRejection) -> Self {
        Self::from(err, 4003)
    }
}

impl From<axum::extract::rejection::QueryRejection> for AppError {
    /// Axum异常
    fn from(err: axum::extract::rejection::QueryRejection) -> Self {
        Self::from(err, 4004)
    }
}

impl From<axum::extract::rejection::TypedHeaderRejection> for AppError {
    /// Axum异常
    fn from(err: axum::extract::rejection::TypedHeaderRejection) -> Self {
        Self::from(err, 4005)
    }
}

impl From<async_session::Error> for AppError {
    /// Session异常
    fn from(err: async_session::Error) -> Self {
        Self::from(err, 5001)
    }
}

impl From<jsonwebtoken::errors::Error> for AppError {
    /// jwt异常
    fn from(err: jsonwebtoken::errors::Error) -> Self {
        Self::from(err, 6001)
    }
}

impl From<askama::Error> for AppError {
    /// 模板异常
    fn from(err: askama::Error) -> Self {
        Self::from(err, 7001)
    }
}

impl IntoResponse for AppError {
    fn into_response(self) -> axum::response::Response {
        tracing::error!("{:?}", self);
        CusResponse::<String>::error_with_code(self.message, self.code).into_response()
    }
}

